Dynamically Populating an ExpandingMenu Using Ajax Callbacks

Description

The Expanding Menu control can be populated using server-side Xbasic. This allows you to create a dynamic menu at run-time based on data in a SQL database or computed from an Ajax Callback.

Two Xbasic helper functions are available for generating the JSON required to populate the Expanding Menu control. The first, a5_sqlToJSONExpandingMenu(), helps populate the Expanding Menu using data from a SQL query. The second, a5wcb_convertFriendlyMenuToExpandingMenuJSONData(), generates the JSON for the Expanding Menu from a list of menu options defined as a CR-LF delimited list with tab indentation to indicate nesting.

Populating from a SQL Database

You can dynamically generate the menu options in an Expanding Menu at runtime from a SQL database using the a5_sqlToJSONExpandingMenu() function. The function takes a connection string, table, and list of fields to fetch from the table. The list of fields to fetch must be specified in the order that you would like to nest the menu options. For example, if you specify a single field, the generated menu will be a flat list of values from the specified field:

dim cs as c = "::Name::AADemo-Northwind"
dim table as c = "Customers"
dim fields as c = "contactname"

dim json as c
json = a5_sqlToJSONExpandingMenu(cs, table, fields)

The above generates the JSON to display a list of contact name's from the Customers table, which can then be sent back to the client to populate the Expanding Menu.

images/customersExpandingMenuSimple.png

See full example at the end of this section for the JavaScript to populate the Expanding Menu control.

If you wanted to group contacts by the city and country in the Expanding Menu, you can specify additional fields in the field list:

dim cs as c = "::Name::AADemo-Northwind"
dim table as c = "Customers"
dim fields as c = "country,city,contactname"
dim filter as c = "country <> ''"
dim order as c = "country"

json = a5_sqlToJSONExpandingMenu(cs, table, fields,"",filter,order)

This creates the JSON to generate the following Expanding Menu with the top level menu options are the countries with the city for each country nested in a sub-menu and the contact name for the city listed in a sub-menu option below the city:

images/customersExpandingMenu.png

You can optionally specify a filter, order, any arguments used in the filter or order expression, and a JavaScript function to call when a leaf node is clicked. You can also define an "endpoint field", which is the field that contains the value to pass to the JavaScript function.

The example below shows how to expand the example above to call the JavaScript function "selectCustomer" and pass it the customerID when a leaf node in the Expanding Menu is clicked (this example also shows how to generate the JavaScript to populate the Expanding Menu control):

function xbSQL as c (e as p)
    ' This Ajax Callback Function repopulates an Expanding Menu Control
    ' in the UX Component with data from a SQL Database.

    dim cs as c = "::name::AADemo-Northwind"
    dim table as c = "customers"
    dim fields as c = "country,city,contactname"
    dim endpointField as c = "customerId"
    dim filter as c = "country <> ''"
    dim order as c = "country"
    dim endpointFunctionName as c = "selectCustomer"

    dim json as c 
    json = a5_sqlToJSONExpandingMenu(cs,table,fields,endpointField,filter,order,null_value(),endpointFunctionName)

    ' Generate the JavaScript to Populate the Expanding Menu Control:
    dim js as c 
    js = "var data = " + json + ";" + crlf()
    js = js + <<%str%
    var obj = {dialog.object}.getControl('__controlid__');
    obj.data.items = data.items
    obj.data.actions = data.actions
    obj.refresh()
    %str%

    dim controlId as c = "expandingmenu"
    js = stritran(js,"__controlId__",controlId)

    xbSQL =  js

end function

Here is the selectCustomer JavaScript function, which can be defined in the Javascript Functions section of the UX Component:

function selectCustomer(txt) { 
    alert('select: ' + txt);
}

Click here to download a component that implements the example show above or watch the video in the Videos section to learn more.

Populating from a Menu Definition

JSON needed to populate an Expanding Menu control can also be generated from a menu definition that uses a CRLF list of menu entries, with tab indentation to indicate menu hierarchy.

The menu definition allows you to assign a optional server-side show/hide expression and/or a set of security groups to any menu branch or leaf item. The JSON that is generated will honor the server-side show/hide expressions and security groups. For example, if a menu branch should only be available to someone who is a member of the Administrators group, the generated JSON will not include that branch if the currently logged in user is not in the Administrators group.

The example below shows a "friendly" menu definition:

Alpha
	Alpha1
	Alpha2
		Alpha21

		Alpha22
Beta
	Beta1
Gamma

    Gamma1

Each item in the menu can be followed by an optional JSON definition. This JSON definition can have these properties (all of which are optional):

icon

The icon to show to the left of menu item in the Expanding Menu

onclick

The Javascript code to execute when the menu item is clicked

serverSideShowHideExpn

The server-side expression. If this expression is false, the menu item is not shown

securityGroups

A comma delimited list of security groups. If the user is not in one of the security groups, the menu item is not shown.

The above "friendly" menu definition shown using the optional JSON settings string for some of the menu items:

Alpha{serverSideShowHideExpn: 'session.var1 = "alpha"'}
	Alpha1
	Alpha2
		Alpha21{securityGroups: 'Administrator,Sales'}
		Alpha22
Beta
	Beta1
Gamma{icon: 'svgIcon=#alpha-icon-bandAidCrossed:icon,24'}
	Gamma1

The a5wcb_convertFriendlyMenuToExpandingMenuJSONData() function will take the above menu definition and translate it into the JSON that can be used to populate the Expanding Menu control:

dim menu as c=<<%str%
Alpha{serverSideShowHideExpn: 'session.var1 = "alpha"'}
	Alpha1
	Alpha2
		Alpha21{securityGroups: 'Administrator,Sales'}
		Alpha22
Beta
	Beta1
Gamma{icon: 'svgIcon=#alpha-icon-bandAidCrossed:icon,24'}
	Gamma1
%str%

json = a5wcb_convertFriendlyMenuToExpandingMenuJSONData(menu)

showvar(json)

A more complex example is shown below.

function xb as c (e as p)
    dim data as c
    dim menu as c

    'the menu definition is a crlf string with tab indents to indicate the menu hierarchy
    'each menu item can be followed by an optional JSON string with these properties:
    'icon - an icon to show befor the menu text
    'security - the security groups for the menu. if the user is not a member of the security group, the menu item is hidden
    'serverSideShowHideExpn - a server side expression that determines if the menu item is shown or hidden (e.g. session.user = "fred")

    menu = <<%str%
Alpha{icon: 'svgIcon=#alpha-icon-bandAidCrossed:icon,24'}
	A1{security: 'alpha',onclick:'alert(\'You clicked on A1\')', icon: 'svgIcon=#alpha-icon-basketAddSolid:icon,24'}
	A2{icon: 'svgIcon=#alpha-icon-clock:icon,24'}
	A3{icon: 'svgIcon=#alpha-icon-deliveryRemove:icon,24'}
Beta{serverSideShowHideExpn: "1=1", icon: 'svgIcon=#alpha-icon-dataJSON:icon,24'}
	B1{onclick: 'alert(\'B1\');', icon: 'svgIcon=#alpha-icon-record:icon,24'}
	B2{onclick: 'alert(\'B2\');', icon: 'svgIcon=#alpha-icon-trash:icon,24'}
	B3{icon: 'svgIcon=#alpha-icon-starHalfSolid:icon,24'}
		B3.1{icon: 'svgIcon=#alpha-icon-speaker:icon,24'}
		B3.2{icon: 'svgIcon=#alpha-icon-speaker:icon,24'}
		B3.3{onclick: 'alert(\'B3.3\');', icon: 'svgIcon=#alpha-icon-speaker:icon,24'}
Gamma{icon: 'svgIcon=#alpha-icon-speaker:icon,24'}
%str%


    'convert the menu definition into a JSON format required by the Expanding Menu.
    '(any menu items that must be hidden because of the serverSideShowHideExpn or by security will be removed from the JSON data)
    json = a5wcb_convertFriendlyMenuToExpandingMenuJSONData(menu,e.tmpl)
    dim js as c

    'generate the Javascript to populate the Expanding Menu Control
    js = "var data = " + json + ";" + crlf()
    js = js + <<%str%
    var obj = {dialog.object}.getControl('__controlid__');
    obj.data.items = data.items
    obj.data.actions = data.actions
    obj.refresh()	
    %str%
    dim controlId as c = "expandingmenu"
    js = stritran(js,"__controlId__",controlId)
    xb = js 

    'TIP: Instead of generating the Javascript as shown above, you can use a helper function that generates the Javascript
    'dim controlId as c = "expandingMenu"
    'xb =  a5DialogHelper_PopulateExpandingMenuControl(menu,controlId,e.tmpl)

end function

Videos

Dynamically Populating an Expanding Menu with Ajax Callbacks

This video shows how you can dynamically populate an Expanding Menu control with data computed in an Ajax callback.

Download Component

2018-08-09

See Also